﻿using System;
using System.ComponentModel;
using System.IO;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.ServiceModel.DomainServices.Client;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Composite.Presentation.Commands;
using TelecoSystems.Infrastructure.Helpers;
using TelecoSystems.Infrastructure.Views;
using TelecoSystems.Web.Models;
using TelecoSystems.Web.Services.Products;
using TelecoSystems.Web.Services.Variants;

namespace TelecoSystems.Modules.Products.ViewModels {
    public partial class ProductsBrowseViewModel : PagedViewModelBase {
        #region Constructors and Factory Methods
        public ProductsBrowseViewModel(ProductsDomainContext productsCtx, VariantsDomainContext variantsCtx) {
            _productsCtx = productsCtx;
            _variantsCtx = variantsCtx;

            _productsCtx.AddReference(typeof(Variant), _variantsCtx);

            _productsCtx.PropertyChanged += (s, e) => {
                NotifyPropertyChanged("ContextHasChanges");
            };

            Products = new PagedEntityCollectionView<Product>(_productsCtx.Products, this);
            TargetGroups = new EntityCollectionView<TargetGroup>(_variantsCtx.TargetGroups);
            //Variants = new EntityCollectionView<Variant>(_variantsCtx.Variants);

            SubmitChangesCommand = new DelegateCommand<object>(SubmitChanges, CanSubmitChanges);
            RejectChangesCommand = new DelegateCommand<object>(RejectChanges, CanRejectChanges);

            EditVariantCommand = new DelegateCommand<object>(EditVariant, CanEditVariant);
            AddVariantCommand = new DelegateCommand<object>(AddVariant, CanAddVariant);
            DeleteVariantCommand = new DelegateCommand<object>(DeleteVariant, CanDeleteVariant);

            ExportCommand = new DelegateCommand<string>(Export, s => { return true/*!Products.IsEmpty*/; });
            PrintCommand = new DelegateCommand<object>(Print, s => { return true; });

            Products.Refreshed += ProductsRefreshed;
            //Products.CurrentChanged += ProductsCurrentChanged;

            PageSize = 2;

            LoadData();
        }
        #endregion

        #region Data
        public override void LoadData() {
            if (IsLoading || _productsCtx == null) {
                return;
            }

            IsBusy = true;
            IsLoading = true;

            _productsCtx.Products.Clear();
            var q = _productsCtx.GetProducsQuery();

            if (Products.SortDescriptions.Count > 0) {
                bool isFirst = true;
                foreach (SortDescription sd in Products.SortDescriptions) {
                    q = OrderBy(q, isFirst, sd.PropertyName, sd.Direction == ListSortDirection.Descending);
                    isFirst = false;
                }
            } else {
                q = q.OrderBy(p => p.Id);
            }

            if (PageSize > 0) {
                q = q.Skip(PageSize * PageIndex);
                q = q.Take(PageSize);
            }

            if (TotalItemCount <= 0) q.IncludeTotalCount = true;

            _productsCtx.Load(q, ProductsLoaded, null);

            var vq = _variantsCtx.GetVariantsQuery();
            _variantsCtx.Load(vq, VariantsLoaded, null);
        }
        #endregion

        #region VM Properties
        public string HeaderInfo { get { return "Browse products"; } }

        public Visibility ContextHasChanges {
            get {
                if (SubmitChangesCommand != null)
                    SubmitChangesCommand.RaiseCanExecuteChanged();
                if (RejectChangesCommand != null)
                    RejectChangesCommand.RaiseCanExecuteChanged();

                if (_productsCtx == null) return Visibility.Collapsed;                
                return _productsCtx.HasChanges ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        private PagedEntityCollectionView<Product> _products;
        public PagedEntityCollectionView<Product> Products {
            get { return _products; }
            set {
                if (_products != value) {
                    _products = value;
                    NotifyPropertyChanged("Products");
                    if (SubmitChangesCommand != null)
                        SubmitChangesCommand.RaiseCanExecuteChanged();
                    if (RejectChangesCommand != null)
                        RejectChangesCommand.RaiseCanExecuteChanged();
                }
            }
        }

        private Product productDetail;
        public Product ProductDetail {
            get {
                //LoadVariants(productDetail);
                return productDetail;
            }
            set {
                if (productDetail != value) {
                    Products.MoveCurrentTo(value);
                    productDetail = value;
                    NotifyPropertyChanged("ProductDetail");
                    if (SubmitChangesCommand != null)
                        SubmitChangesCommand.RaiseCanExecuteChanged();
                    if (RejectChangesCommand != null)
                        RejectChangesCommand.RaiseCanExecuteChanged();
                    if (EditVariantCommand != null)
                        EditVariantCommand.RaiseCanExecuteChanged();
                }
            }
        }

        private Variant _variantDetail;
        public Variant VariantDetail {
            get {
                return _variantDetail;
            }
            set {
                if (_variantDetail != value) {
                    _variantDetail = value;
                    NotifyPropertyChanged("VariantDetail");
                    if (EditVariantCommand != null)
                        EditVariantCommand.RaiseCanExecuteChanged();
                }
            }
        }

        private EntityCollectionView<TargetGroup> _targetGroups;
        public EntityCollectionView<TargetGroup> TargetGroups {
            get {
                return _targetGroups;
            }
            set {
                if (_targetGroups != value) {
                    _targetGroups = value;
                    NotifyPropertyChanged("TargetGroups");
                }
            }
        }

        private bool _isBusy = false;
        public bool IsBusy {
            get { return _isBusy; }
            set {
                if (_isBusy != value) {
                    _isBusy = value;
                    NotifyPropertyChanged("IsBusy");
                }
            }
        }

        private string _busyContent = "Please wait...";
        public string BusyContent {
            get { return _busyContent; }
            set {
                if (_busyContent != value) {
                    _busyContent = value;
                    NotifyPropertyChanged("BusyContent");
                }
            }
        }
        #endregion

        #region Commands (in partial)
        //Commands are located in Commands/ProductsBrowseCommands.cs
        #endregion

        #region Private Methods
        private string Serialize<T>(T data) {
            using (var memoryStream = new MemoryStream()) {
                var serializer = new DataContractSerializer(typeof(T));
                serializer.WriteObject(memoryStream, data);

                memoryStream.Seek(0, SeekOrigin.Begin);

                var reader = new StreamReader(memoryStream);
                string content = reader.ReadToEnd();
                return content;
            }
        }

        private void ExportToXML() {
            var dialog = new SaveFileDialog();

            dialog.DefaultExt = "*.xml";
            dialog.Filter = "Xml document (*.xml)|*.xml|All files (*.*)|*.*";

            if (dialog.ShowDialog() ?? false) {
                Stream fileStream = dialog.OpenFile();

                var sw = new StreamWriter(fileStream);

                string xml = Serialize<EntitySet<Product>>(_productsCtx.Products);

                sw.Write(xml);

                sw.Close();
            }
        }

        private EntityQuery<Product> OrderBy(EntityQuery<Product> q, bool isFirst, string propertyName, bool descending) {
            Expression<Func<Product, object>> sortExpression;

            switch (propertyName) {
                case "Name":
                    sortExpression = p => p.Name;
                    break;
                case "Id":
                    sortExpression = p => p.Id;
                    break;
                case "Description":
                    sortExpression = p => p.Description;
                    break;
                case "Modified":
                    sortExpression = p => p.DateLastModified;
                    break;
                case "Created":
                    sortExpression = p => p.DateCreated;
                    break;
                default:
                    sortExpression = p => p.Id;
                    break;
            }

            if (isFirst) {
                if (descending)
                    return q.OrderByDescending(sortExpression);

                return q.OrderBy(sortExpression);
            } else {
                if (!descending)
                    return q.ThenByDescending(sortExpression);

                return q.ThenBy(sortExpression);
            }
        }

        #endregion

        #region Event Handlers
        private void ProductsLoaded(LoadOperation<Product> loadOperation) {
            if (loadOperation.HasError) {
                ErrorWindow.CreateNew(loadOperation.Error, StackTracePolicy.OnlyWhenDebuggingOrRunningLocally);
            } else {
                Products.MoveCurrentToFirst();
                if (loadOperation.TotalEntityCount > 0) {
                    ItemCount = loadOperation.TotalEntityCount;
                    TotalItemCount = loadOperation.TotalEntityCount;
                    InformationWindow.Create("Products were loaded successfully.", "Notification");
                }                
            }
            IsLoading = false;
            IsBusy = false;
        }

        private void VariantsLoaded(LoadOperation<Variant> loadOperation) {
            if (loadOperation.HasError) {
                ErrorWindow.CreateNew(loadOperation.Error, StackTracePolicy.OnlyWhenDebuggingOrRunningLocally);
            } else {
                //success
            }

            var q = _variantsCtx.GetTargetGroupsQuery();
            _variantsCtx.Load(q, TargetGroupsLoaded, null);
        }

        private void TargetGroupsLoaded(LoadOperation<TargetGroup> loadOperation) {
            if (loadOperation.HasError) {
                ErrorWindow.CreateNew(loadOperation.Error, StackTracePolicy.OnlyWhenDebuggingOrRunningLocally);
            } else {
                //success                
            }
        }

        private void SubmitChangesCompleted(SubmitOperation submit) {
            if (submit.HasError) {
                foreach (Entity entity in submit.EntitiesInError) {

                }
                ErrorWindow.CreateNew(submit.Error, StackTracePolicy.OnlyWhenDebuggingOrRunningLocally);
            } else {
                var vq = _variantsCtx.GetVariantsQuery();
                _variantsCtx.Load(vq, LoadBehavior.MergeIntoCurrent, VariantsLoaded, null);
                InformationWindow.Create("Changes were saved successfully.", "Information");
            }
            IsBusy = false;
        }

        private void ProductsRefreshed(object sender, EventArgs e) {
            LoadData();
        }

        private void ProductsCurrentChanged(object sender, EventArgs e) {
            //LoadVariants(productDetail);
        }
        #endregion

        #region Private Fields
        private ProductsDomainContext _productsCtx;
        private VariantsDomainContext _variantsCtx;
        #endregion
    }
}
